/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package fsktms.server;

import fsktms.IMessage;
import fsktms.Message;
import fsktms.dispatcher.DispatchSource;
import fsktms.messagemanager.IMessageManager;
import fsktms.messagemanager.MessageFactory;
import fsktms.packetmanager.PacketFactory;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;

/**
 *
 * @author salm
 */
public class NetServer
    implements Runnable, DispatchSource
{
    private Queue<IMessage> messages = new ArrayDeque<>();
    private final Map<String, IMessageManager> subServers = new HashMap<>();
    private ServerSocket sv;
    private boolean closed = false;
    private final Collection<ServerEventListener> listeners = new ArrayList();
    
    public NetServer(int port) throws IOException
    {
        sv = new ServerSocket(port);
    }
    
    @Override
    public IMessage getCompletedMessage() 
    {
        this.updateMessages();
        return messages.poll();
    }

    @Override
    public boolean isHasCompletedMessage()
    {
        this.updateMessages();
        return !this.messages.isEmpty();
    }

    @Override
    public void run()
    {
        System.out.println("Server started; IP: " + this.sv);
        while (!sv.isClosed())
        {
            try
            {
                Socket sk = sv.accept();
                IMessageManager newClient = MessageFactory.createMM(PacketFactory.createPM(sk));
                synchronized (subServers)
                {
                    synchronized (this.listeners)
                    {
                        for (ServerEventListener sei : this.listeners)
                        {
                            sei.clientConnected(this, newClient);
                        }
                    }
                    
                    this.subServers.put(newClient.getAddress(), newClient);
                }
                
                Thread newClientT = new Thread(newClient);
                newClientT.start();
            }
            catch (IOException ex)
            {
                System.err.println("Server can't get socket!!!\n" + ex.getMessage());
            }
        }
    }
    
    public void updateMessages()
    {
        synchronized (subServers)
        {
            Collection<IMessageManager> msgMans = this.subServers.values();
            Collection<String> msgManClosed = new LinkedList<>();

            for (IMessageManager msgMan : msgMans)
            {
                while (msgMan.isHasCompletedMessage())
                {
                    this.messages.add(msgMan.getCompletedMessage());
                }

                if (msgMan.isClosed())
                {
                    msgManClosed.add(msgMan.getAddress());
                }
            }

            for (String msgAdd : msgManClosed)
            {
                synchronized (this.listeners)
                {
                    for (ServerEventListener sei : this.listeners)
                    {
                        sei.clientDisconnected(this, subServers.get(msgAdd));
                    }
                }

                this.subServers.remove(msgAdd);
            }
        }
    }

    public void sendMessage(String destAddress, byte []data, int magicKey)
    {
        IMessageManager subServer = this.subServers.get(destAddress);
        if (subServer != null)
        {
            subServer.sendMessage(new Message(destAddress, data, magicKey));
        }
    }

    @Override
    public void close() throws IOException
    { 
        this.sv.close();
        this.closed = true;
    }
    
    @Override
    public boolean isClosed()
    {
        return this.closed;
    }
    
    public IMessageManager getSubServerOf(String address)
    {
        return this.subServers.get(address);
    }
    
    public Collection<ServerEventListener> getListeners()
    {
        return this.listeners;
    }
}
